/*
 Object.assign(obj1,obj2)
    浅合并两个对象，用 obj2 中的每个成员，去替换 obj1 中的每个成员
       + 两个对象都具备的成员以 obj2 为主
       + obj1有的但obj2没有：依然存在
       + obj1没有但obj2有：给obj1新增这个成员
    修改的是obj1这个对象，obj2对象不变，最后返回的值也是obj1这个对象！ 

 Object.assign(obj1,obj2,obj3,obj4)
    多个对象合并，返回和修改的都是obj1
    @1 obj2 替换 obj1
    @2 obj3 替换 obj1
    ...
 */
/* 
let obj1 = {
    x: 10,
    y: 20
}
let obj2 = {
    y: 30,
    z: 40
}
let res = Object.assign(obj1, obj2)
console.log(res === obj1) //true
console.log(obj1, obj2) 
*/

//=======================
/* 对象的浅拷贝 */
/* let obj = {
    x: 10,
    y: {
        z: 20
    },
    bool: true,
    time: new Date(),
    fn() { },
    [Symbol('AA')]: 'AAA'
} */
// let obj2 = { ...obj }
// let obj2 = Object.assign({}, obj)
/* 
// 笨办法：迭代obj，把每一项分别赋值给obj2
let obj2 = {}
_.each(obj, (value, key) => {
    obj2[key] = value
}) 
*/
// console.log('结果：', obj2, obj)
// console.log(obj2 === obj) //false
// console.log(obj2.y === obj.y) //true
// console.log(obj2.fn === obj.fn) //true

//=======================
/* 数组的浅拷贝 */
/* 
let arr = [10, 20, 30, [40, 50]]
// let arr2 = [...arr]
// let arr2 = Object.assign([], arr)
// let arr2 = [].concat(arr)
// let arr2 = arr.slice(0)
// let arr2 = arr.map(item => item)
console.log(arr, arr2)
console.log(arr === arr2) //false
console.log(arr[3] === arr2[3]) //true 
*/

//===================================
/* 深拷贝的处理 */
/* 
// 对于正常的一些属性值，例如：数字/字符串/布尔/普通对象/数组...，而且属性名都是字符串类型的，我们可以直接基于 JSON.stringify/parse 实现深拷贝！！
// 原理：先把对象变为JSON字符串，再把字符串转换为对象「此过程会把所有级别的内容空间，都重新创建一份新的」
let obj = {
    x: 10,
    y: {
        z: 20,
        arr: [10, 20, { b: 300 }]
    },
    bool: true,
    str: '珠峰培训'
}
let obj2 = JSON.parse(JSON.stringify(obj))
console.log(obj2)
console.log(obj2 === obj) //false
console.log(obj2.y === obj.y) //false
console.log(obj2.y.arr === obj.y.arr) //false
console.log(obj2.y.arr[2] === obj.y.arr[2]) //false 
*/

/*
 JSON.stringify 具备很强的局限性
   @1 无法处理BigInt类型的值「抱错 Do not know how to serialize a BigInt」
   @2 成员值是 undefined/Symbol/函数 的，会直接把此成员删除掉
   @3 把正则/错误对象直接变为 “{}”，这样等到基于 parse 转换为对象的时候，正则值会被处理为空对象
   @4 把日期对象变为日期字符串(但是是基于 日期对象.toJSON 方法处理的，2023-05-09T03:12:02.703Z)，这个字符串基于 parse 转换为对象的时候，是无法再转换回日期对象的！
   @5 属性名是Symbol类型的，也会直接把此成员删除掉
   @6 一但对象出现“套娃”操作，则直接报错 Converting circular structure to JSON
   ...
 */
let obj = {
    name: '珠峰',
    age: 13,
    bool: true,
    n: null,
    u: undefined,
    sym: Symbol('sym'),
    big: 10n,
    list: [10, 20, { a: 100, b: 200 }],
    reg: /\d+/,
    time: new Date,
    err: new Error('xxx'),
    ke: { js: '基础课', web: '高级课', arr: [1000, 2000] },
    [Symbol('KEY')]: 100,
    fn: function () { }
}
obj.obj = obj


/* 实现数组和对象的深拷贝 */
const clone = function clone(obj, exist = []) {
    if (!_.isObject(obj)) return obj  //原始值类型不进行处理
    let type = _.isType(obj),
        Ctor = obj.constructor
    if (type === 'regexp' || type === 'date') return new Ctor(obj)
    if (type === 'error') return new Ctor(obj.message)
    if (!_.isArray(obj) && !_.isPlainObject(obj)) return obj
    // 防止套娃操作
    if (exist.indexOf(obj) >= 0) return obj
    exist.push(obj)
    // 操作的是数组和纯粹对象
    let result = new Ctor()
    _.each(obj, (value, key) => {
        result[key] = clone(value, exist) //基于递归的方式，对每一个成员值，再次进行拷贝
    })
    return result
}

let obj2 = clone(obj)
console.log(obj2)
console.log(obj2 === obj) //false
console.log(obj2.list === obj.list) //false
console.log(obj2.list[2] === obj.list[2]) //false
console.log(obj2.reg === obj.reg) //false
console.log(obj2.fn === obj.fn) //true